"""
AIQuant Web API客户端
统一的API访问接口，提供HTTP请求封装、错误处理、重试机制等功能
"""

import asyncio
import json
import logging
import time
from typing import Any, Dict, List, Optional, Union
from urllib.parse import urljoin

import httpx
import streamlit as st
from tenacity import retry, stop_after_attempt, wait_exponential

logger = logging.getLogger(__name__)


class APIError(Exception):
    """API调用异常"""
    def __init__(self, message: str, status_code: Optional[int] = None, response_data: Optional[Dict] = None):
        self.message = message
        self.status_code = status_code
        self.response_data = response_data or {}
        super().__init__(self.message)


class APIClient:
    """
    统一API客户端
    
    提供以下功能：
    - HTTP请求封装 (GET, POST, PUT, DELETE)
    - 自动认证token管理
    - 请求重试机制
    - 错误处理和状态码映射
    - 响应数据格式化
    - 请求超时控制
    """
    
    def __init__(
        self,
        base_url: str = "http://localhost:8000",
        timeout: int = 30,
        max_retries: int = 3,
        auth_token: Optional[str] = None
    ):
        """
        初始化API客户端
        
        Args:
            base_url: API基础URL
            timeout: 请求超时时间（秒）
            max_retries: 最大重试次数
            auth_token: 认证token
        """
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.max_retries = max_retries
        self.auth_token = auth_token
        
        # 创建HTTP客户端
        self.client = httpx.AsyncClient(
            timeout=httpx.Timeout(timeout),
            headers=self._get_default_headers()
        )
        
        # 请求统计
        self.stats = {
            'total_requests': 0,
            'successful_requests': 0,
            'failed_requests': 0,
            'retry_count': 0,
            'avg_response_time': 0.0
        }
    
    def _get_default_headers(self) -> Dict[str, str]:
        """获取默认请求头"""
        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'User-Agent': 'AIQuant-Web/1.0'
        }
        
        if self.auth_token:
            headers['Authorization'] = f'Bearer {self.auth_token}'
        
        return headers
    
    def _build_url(self, endpoint: str) -> str:
        """构建完整URL"""
        if endpoint.startswith('http'):
            return endpoint
        return urljoin(f"{self.base_url}/", endpoint.lstrip('/'))
    
    def _update_stats(self, success: bool, response_time: float, retried: bool = False):
        """更新请求统计"""
        self.stats['total_requests'] += 1
        if success:
            self.stats['successful_requests'] += 1
        else:
            self.stats['failed_requests'] += 1
        
        if retried:
            self.stats['retry_count'] += 1
        
        # 更新平均响应时间
        total = self.stats['total_requests']
        current_avg = self.stats['avg_response_time']
        self.stats['avg_response_time'] = (current_avg * (total - 1) + response_time) / total
    
    def _handle_response(self, response: httpx.Response) -> Dict[str, Any]:
        """
        处理HTTP响应
        
        Args:
            response: HTTP响应对象
            
        Returns:
            解析后的响应数据
            
        Raises:
            APIError: API调用失败时抛出
        """
        try:
            # 尝试解析JSON响应
            if response.headers.get('content-type', '').startswith('application/json'):
                data = response.json()
            else:
                data = {'message': response.text}
        except json.JSONDecodeError:
            data = {'message': 'Invalid JSON response'}
        
        # 检查HTTP状态码
        if response.status_code == 200:
            return data
        elif response.status_code == 401:
            raise APIError("认证失败，请检查token", response.status_code, data)
        elif response.status_code == 403:
            raise APIError("权限不足，无法访问该资源", response.status_code, data)
        elif response.status_code == 404:
            raise APIError("请求的资源不存在", response.status_code, data)
        elif response.status_code == 422:
            raise APIError("请求参数验证失败", response.status_code, data)
        elif response.status_code >= 500:
            raise APIError("服务器内部错误", response.status_code, data)
        else:
            raise APIError(f"请求失败: {response.status_code}", response.status_code, data)
    
    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=1, max=10),
        reraise=True
    )
    async def _make_request(
        self,
        method: str,
        endpoint: str,
        params: Optional[Dict] = None,
        data: Optional[Dict] = None,
        json_data: Optional[Dict] = None,
        headers: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """
        发送HTTP请求（带重试机制）
        
        Args:
            method: HTTP方法
            endpoint: API端点
            params: URL参数
            data: 表单数据
            json_data: JSON数据
            headers: 额外请求头
            
        Returns:
            响应数据
        """
        url = self._build_url(endpoint)
        request_headers = self._get_default_headers()
        if headers:
            request_headers.update(headers)
        
        start_time = time.time()
        
        try:
            response = await self.client.request(
                method=method,
                url=url,
                params=params,
                data=data,
                json=json_data,
                headers=request_headers
            )
            
            response_time = time.time() - start_time
            result = self._handle_response(response)
            self._update_stats(True, response_time)
            
            logger.debug(f"API请求成功: {method} {url} ({response_time:.2f}s)")
            return result
            
        except httpx.TimeoutException:
            response_time = time.time() - start_time
            self._update_stats(False, response_time, True)
            raise APIError(f"请求超时 ({self.timeout}秒)", 408)
        
        except httpx.ConnectError:
            response_time = time.time() - start_time
            self._update_stats(False, response_time, True)
            raise APIError("无法连接到服务器，请检查网络连接", 503)
        
        except APIError:
            response_time = time.time() - start_time
            self._update_stats(False, response_time, True)
            raise
        
        except Exception as e:
            response_time = time.time() - start_time
            self._update_stats(False, response_time, True)
            logger.error(f"API请求异常: {method} {url} - {str(e)}")
            raise APIError(f"请求异常: {str(e)}")
    
    async def get(
        self,
        endpoint: str,
        params: Optional[Dict] = None,
        headers: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """
        发送GET请求
        
        Args:
            endpoint: API端点
            params: URL参数
            headers: 额外请求头
            
        Returns:
            响应数据
        """
        return await self._make_request('GET', endpoint, params=params, headers=headers)
    
    async def post(
        self,
        endpoint: str,
        data: Optional[Dict] = None,
        json_data: Optional[Dict] = None,
        headers: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """
        发送POST请求
        
        Args:
            endpoint: API端点
            data: 表单数据
            json_data: JSON数据
            headers: 额外请求头
            
        Returns:
            响应数据
        """
        return await self._make_request('POST', endpoint, data=data, json_data=json_data, headers=headers)
    
    async def put(
        self,
        endpoint: str,
        data: Optional[Dict] = None,
        json_data: Optional[Dict] = None,
        headers: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """
        发送PUT请求
        
        Args:
            endpoint: API端点
            data: 表单数据
            json_data: JSON数据
            headers: 额外请求头
            
        Returns:
            响应数据
        """
        return await self._make_request('PUT', endpoint, data=data, json_data=json_data, headers=headers)
    
    async def delete(
        self,
        endpoint: str,
        params: Optional[Dict] = None,
        headers: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """
        发送DELETE请求
        
        Args:
            endpoint: API端点
            params: URL参数
            headers: 额外请求头
            
        Returns:
            响应数据
        """
        return await self._make_request('DELETE', endpoint, params=params, headers=headers)
    
    def get_stats(self) -> Dict[str, Any]:
        """获取请求统计信息"""
        return self.stats.copy()
    
    async def health_check(self) -> bool:
        """
        健康检查
        
        Returns:
            服务是否可用
        """
        try:
            await self.get('/health')
            return True
        except APIError:
            return False
    
    async def close(self):
        """关闭HTTP客户端"""
        await self.client.aclose()


# 全局API客户端实例
_api_client: Optional[APIClient] = None


def get_api_client() -> APIClient:
    """
    获取全局API客户端实例
    
    Returns:
        API客户端实例
    """
    global _api_client
    
    if _api_client is None:
        # 从Streamlit配置中获取设置
        base_url = st.secrets.get("api", {}).get("base_url", "http://localhost:8000")
        timeout = st.secrets.get("api", {}).get("timeout", 30)
        auth_token = st.secrets.get("api", {}).get("auth_token")
        
        _api_client = APIClient(
            base_url=base_url,
            timeout=timeout,
            auth_token=auth_token
        )
    
    return _api_client


def sync_api_call(coro):
    """
    同步调用异步API方法
    
    Args:
        coro: 异步协程
        
    Returns:
        API响应结果
    """
    try:
        loop = asyncio.get_event_loop()
        if loop.is_running():
            # 如果事件循环正在运行，创建新的任务
            import concurrent.futures
            with concurrent.futures.ThreadPoolExecutor() as executor:
                future = executor.submit(asyncio.run, coro)
                return future.result()
        else:
            return loop.run_until_complete(coro)
    except RuntimeError:
        # 如果没有事件循环，创建新的
        return asyncio.run(coro)


# 便捷函数
def api_get(endpoint: str, params: Optional[Dict] = None) -> Dict[str, Any]:
    """同步GET请求"""
    client = get_api_client()
    return sync_api_call(client.get(endpoint, params))


def api_post(endpoint: str, data: Optional[Dict] = None, json_data: Optional[Dict] = None) -> Dict[str, Any]:
    """同步POST请求"""
    client = get_api_client()
    return sync_api_call(client.post(endpoint, data, json_data))


def api_put(endpoint: str, data: Optional[Dict] = None, json_data: Optional[Dict] = None) -> Dict[str, Any]:
    """同步PUT请求"""
    client = get_api_client()
    return sync_api_call(client.put(endpoint, data, json_data))


def api_delete(endpoint: str, params: Optional[Dict] = None) -> Dict[str, Any]:
    """同步DELETE请求"""
    client = get_api_client()
    return sync_api_call(client.delete(endpoint, params))


def check_api_health() -> bool:
    """检查API服务健康状态"""
    client = get_api_client()
    return sync_api_call(client.health_check())


if __name__ == "__main__":
    # 测试代码
    async def test_api_client():
        client = APIClient()
        
        try:
            # 测试健康检查
            is_healthy = await client.health_check()
            print(f"API健康状态: {is_healthy}")
            
            # 测试GET请求
            result = await client.get('/api/system/status')
            print(f"系统状态: {result}")
            
            # 获取统计信息
            stats = client.get_stats()
            print(f"请求统计: {stats}")
            
        except APIError as e:
            print(f"API错误: {e.message} (状态码: {e.status_code})")
        
        finally:
            await client.close()
    
    # 运行测试
    asyncio.run(test_api_client())